use bevy::color::palettes::css::*;
use bevy::render::render_asset::RenderAssetUsages;
use bevy::{
    prelude::*,
    render::{mesh::Indices, render_resource::PrimitiveTopology, view::RenderLayers},
};

use crate::{generate_corner_face, PI_2, PI_4, PI_4_3};

use super::{simple_viewcube::generate_viewcube_simple_face, CubePart, ViewcubeHit};

#[derive(Clone, Copy)]
struct ViewcubeEdge(pub CubePart);

#[derive(Clone, Copy)]
struct ViewcubeCorner(pub CubePart);

impl From<ViewcubeEdge> for Mesh {
    fn from(value: ViewcubeEdge) -> Self {
        let mut width = 0.6f32 / 2.0;
        let mut height = 0.1 * 2f32.sqrt() / 2.0;

        let (q, swap) = match value.0 {
            CubePart::FrontTop => (Quat::from_rotation_x(PI_4), false),
            CubePart::FrontBottom => (Quat::from_rotation_x(PI_4_3), false),
            CubePart::BackTop => (Quat::from_rotation_x(-PI_4), false),
            CubePart::BackBottom => (Quat::from_rotation_x(-PI_4_3), false),
            CubePart::LeftTop => (Quat::from_rotation_z(PI_4), true),
            CubePart::LeftBottom => (Quat::from_rotation_z(PI_4_3), true),
            CubePart::RightTop => (Quat::from_rotation_z(-PI_4), true),
            CubePart::RightBottom => (Quat::from_rotation_z(-PI_4_3), true),
            CubePart::FrontLeft => (Quat::from_euler(EulerRot::ZYX, PI_2, 0.0, PI_4), false),
            CubePart::FrontRight => (Quat::from_euler(EulerRot::ZYX, PI_2, 0.0, PI_4_3), false),
            CubePart::BackLeft => (Quat::from_euler(EulerRot::ZYX, PI_2, 0.0, -PI_4), false),
            CubePart::BackRight => (Quat::from_euler(EulerRot::ZYX, PI_2, 0.0, -PI_4_3), false),
            _ => panic!(),
        };

        if swap {
            (width, height) = (height, width);
        }
        let dist_edge: f32 = (2.0 * 0.4f32.powi(2)).sqrt() - 0.1 * 2f32.sqrt() / 2.0;
        let positions = vec![
            Vec3::new(width, dist_edge, height),
            Vec3::new(width, dist_edge, -height),
            Vec3::new(-width, dist_edge, -height),
            Vec3::new(-width, dist_edge, height),
        ]
        .iter()
        .map(|v| q.mul_vec3(*v))
        .collect::<Vec<_>>();

        let uvs = vec![[0f32, 0.0], [0.0, 1.0], [1.0, 1.0], [1.0, 0.0]];
        let indices = vec![0, 1, 2, 2, 3, 0];
        let normal = q.mul_vec3(Vec3::Y);
        let mut normals = vec![];
        normals.resize_with(4, || normal);

        let mut mesh = Mesh::new(
            PrimitiveTopology::TriangleList,
            RenderAssetUsages::default(),
        );
        mesh.insert_indices(Indices::U32(indices));
        mesh.insert_attribute(Mesh::ATTRIBUTE_POSITION, positions);
        mesh.insert_attribute(Mesh::ATTRIBUTE_NORMAL, normals);
        mesh.insert_attribute(Mesh::ATTRIBUTE_UV_0, uvs);
        mesh
    }
}

impl From<ViewcubeCorner> for Mesh {
    fn from(value: ViewcubeCorner) -> Self {
        let edge = 0.1 * 2f32.sqrt();
        let a = edge / 2.0;
        let b = a * 3f32.sqrt();

        let d = 1.0 / 3.0f32;

        let (q, c) = match value.0 {
            CubePart::FrontLeftTop => (
                Quat::from_euler(EulerRot::YXZ, -PI_4, PI_4, 0.0),
                Vec3::new(-d, d, d),
            ),
            CubePart::FrontLeftBottom => (
                Quat::from_euler(EulerRot::YXZ, PI_4_3, -PI_4_3, 0.0),
                Vec3::new(-d, -d, d),
            ),
            CubePart::FrontRightTop => (
                Quat::from_euler(EulerRot::YXZ, PI_4, PI_4, 0.0),
                Vec3::new(d, d, d),
            ),
            CubePart::FrontRightBottom => (
                Quat::from_euler(EulerRot::YXZ, -PI_4_3, -PI_4_3, 0.0),
                Vec3::new(d, -d, d),
            ),
            CubePart::BackLeftTop => (
                Quat::from_euler(EulerRot::YXZ, -PI_4_3, PI_4, 0.0),
                Vec3::new(-d, d, -d),
            ),
            CubePart::BackLeftBottom => (
                Quat::from_euler(EulerRot::YXZ, PI_4, -PI_4_3, 0.0),
                Vec3::new(-d, -d, -d),
            ),
            CubePart::BackRightTop => (
                Quat::from_euler(EulerRot::YXZ, PI_4_3, PI_4, 0.0),
                Vec3::new(d, d, -d),
            ),
            CubePart::BackRightBottom => (
                Quat::from_euler(EulerRot::YXZ, -PI_4, -PI_4_3, 0.0),
                Vec3::new(d, -d, -d),
            ),
            _ => panic!(),
        };

        let positions = vec![
            Vec3::new(0.0, 0.0, -b / 3.0 * 2.0),
            Vec3::new(-a, 0.0, b / 3.0),
            Vec3::new(a, 0.0, b / 3.0),
        ]
        .iter()
        .map(|v| q.mul_vec3(*v) + c)
        .collect::<Vec<_>>();

        let uvs = vec![[0f32, 0.0], [0.0, 1.0], [1.0, 1.0]];
        let indices = vec![0u32, 1, 2];
        let normal = q.mul_vec3(Vec3::Y);
        let mut normals = vec![];
        normals.resize_with(3, || normal);

        let mut mesh = Mesh::new(
            PrimitiveTopology::TriangleList,
            RenderAssetUsages::default(),
        );
        mesh.insert_indices(Indices::U32(indices));
        mesh.insert_attribute(Mesh::ATTRIBUTE_POSITION, positions);
        mesh.insert_attribute(Mesh::ATTRIBUTE_NORMAL, normals);
        mesh.insert_attribute(Mesh::ATTRIBUTE_UV_0, uvs);
        mesh
    }
}

#[derive(Clone, Resource)]
pub struct CubeMaterial {
    pub blank_material: Handle<StandardMaterial>,
    pub corner_edge_hover: Handle<StandardMaterial>,
}

impl FromWorld for CubeMaterial {
    fn from_world(world: &mut World) -> Self {
        let mut materials = world.resource_mut::<Assets<StandardMaterial>>();
        Self {
            blank_material: materials.add(StandardMaterial::default()),
            corner_edge_hover: materials.add(StandardMaterial {
                base_color: Color::srgba(0.7, 0.9, 1.0, 1.0), // Light blue
                ..Default::default()
            }),
        }
    }
}

pub fn setup(
    mut commands: Commands,
    mut meshes: ResMut<Assets<Mesh>>,
    mut materials: ResMut<Assets<StandardMaterial>>,
    mut asset_server: ResMut<AssetServer>,
) {
    let center = Vec3::new(0.6, 0.6, 0.6);
    let blank_matt = materials.add(StandardMaterial::default());

    commands
        .spawn((
            Mesh3d(meshes.add(bevy::prelude::Sphere::new(0.01).mesh().uv(1, 1))),
            MeshMaterial3d(materials.add(StandardMaterial::default())),
            RenderLayers::layer(13),
            super::ViewcubeCenter,
        ))
        .with_children(|builder| {
            builder.spawn((
                Mesh3d(meshes.add(crate::prelude::BevyTridentAxis::default())),
                MeshMaterial3d(materials.add(StandardMaterial::default())),
                Transform::from_translation(-center),
                RenderLayers::layer(13),
            ));
            generate_viewcube_simple_face(
                0.6f32,
                builder,
                &mut meshes,
                &mut materials,
                &mut asset_server,
            );
            generate_corner_face!(
                builder,
                meshes,
                blank_matt,
                ViewcubeEdge(CubePart::FrontTop),
                Transform::IDENTITY,
                ViewcubeHit(CubePart::FrontTop)
            );
            generate_corner_face!(
                builder,
                meshes,
                blank_matt,
                ViewcubeEdge(CubePart::FrontBottom),
                Transform::IDENTITY,
                ViewcubeHit(CubePart::FrontBottom)
            );
            generate_corner_face!(
                builder,
                meshes,
                blank_matt,
                ViewcubeEdge(CubePart::BackTop),
                Transform::IDENTITY,
                ViewcubeHit(CubePart::BackTop)
            );
            generate_corner_face!(
                builder,
                meshes,
                blank_matt,
                ViewcubeEdge(CubePart::BackBottom),
                Transform::IDENTITY,
                ViewcubeHit(CubePart::BackBottom)
            );
            generate_corner_face!(
                builder,
                meshes,
                blank_matt,
                ViewcubeEdge(CubePart::LeftTop),
                Transform::IDENTITY,
                ViewcubeHit(CubePart::LeftTop)
            );
            generate_corner_face!(
                builder,
                meshes,
                blank_matt,
                ViewcubeEdge(CubePart::LeftBottom),
                Transform::IDENTITY,
                ViewcubeHit(CubePart::LeftBottom)
            );
            generate_corner_face!(
                builder,
                meshes,
                blank_matt,
                ViewcubeEdge(CubePart::RightTop),
                Transform::IDENTITY,
                ViewcubeHit(CubePart::RightTop)
            );
            generate_corner_face!(
                builder,
                meshes,
                blank_matt,
                ViewcubeEdge(CubePart::RightBottom),
                Transform::IDENTITY,
                ViewcubeHit(CubePart::RightBottom)
            );
            generate_corner_face!(
                builder,
                meshes,
                blank_matt,
                ViewcubeEdge(CubePart::FrontLeft),
                Transform::IDENTITY,
                ViewcubeHit(CubePart::FrontLeft)
            );
            generate_corner_face!(
                builder,
                meshes,
                blank_matt,
                ViewcubeEdge(CubePart::FrontRight),
                Transform::IDENTITY,
                ViewcubeHit(CubePart::FrontRight)
            );
            generate_corner_face!(
                builder,
                meshes,
                blank_matt,
                ViewcubeEdge(CubePart::BackLeft),
                Transform::IDENTITY,
                ViewcubeHit(CubePart::BackLeft)
            );
            generate_corner_face!(
                builder,
                meshes,
                blank_matt,
                ViewcubeEdge(CubePart::BackRight),
                Transform::IDENTITY,
                ViewcubeHit(CubePart::BackRight)
            );
            generate_corner_face!(
                builder,
                meshes,
                blank_matt,
                ViewcubeCorner(CubePart::FrontLeftTop),
                Transform::IDENTITY,
                ViewcubeHit(CubePart::FrontLeftTop)
            );
            generate_corner_face!(
                builder,
                meshes,
                blank_matt,
                ViewcubeCorner(CubePart::FrontLeftBottom),
                Transform::IDENTITY,
                ViewcubeHit(CubePart::FrontLeftBottom)
            );
            generate_corner_face!(
                builder,
                meshes,
                blank_matt,
                ViewcubeCorner(CubePart::FrontRightTop),
                Transform::IDENTITY,
                ViewcubeHit(CubePart::FrontRightTop)
            );
            generate_corner_face!(
                builder,
                meshes,
                blank_matt,
                ViewcubeCorner(CubePart::FrontRightBottom),
                Transform::IDENTITY,
                ViewcubeHit(CubePart::FrontRightBottom)
            );
            generate_corner_face!(
                builder,
                meshes,
                blank_matt,
                ViewcubeCorner(CubePart::BackLeftTop),
                Transform::IDENTITY,
                ViewcubeHit(CubePart::BackLeftTop)
            );
            generate_corner_face!(
                builder,
                meshes,
                blank_matt,
                ViewcubeCorner(CubePart::BackLeftBottom),
                Transform::IDENTITY,
                ViewcubeHit(CubePart::BackLeftBottom)
            );
            generate_corner_face!(
                builder,
                meshes,
                blank_matt,
                ViewcubeCorner(CubePart::BackRightTop),
                Transform::IDENTITY,
                ViewcubeHit(CubePart::BackRightTop)
            );
            generate_corner_face!(
                builder,
                meshes,
                blank_matt,
                ViewcubeCorner(CubePart::BackRightBottom),
                Transform::IDENTITY,
                ViewcubeHit(CubePart::BackRightBottom)
            );
        });
}
